package mint.modules.generator.expression;

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;

import org.antlr.runtime.tree.Tree;

import mint.MintLexer;
import mint.modules.generator.CounterManager;
import mint.modules.generator.Scope;
import mint.modules.symbol.ClassSymbol;
import mint.modules.symbol.FieldSymbol;
import mint.modules.symbol.MethodSymbol;
import mint.modules.symbol.ParameterSymbol;
import mint.modules.symbol.VariableSymbol;
import mint.modules.symbol.Symbol;
import mint.modules.symbol.SymbolType;
import mint.modules.type.Type;
import mint.modules.type.TypeId;

public class NamePrimaryExprNode extends Expression {
	private String name;
	private List<String> memberaccess;
	private List<Expression> arguments;
	private Expression arrayAccess;
	private List<String> errors;
	private int line;
	private Type type;
	private String leftEmit;	

	public NamePrimaryExprNode(Scope theParent, ExpressionType exprType, Tree nameTree) {
		super(theParent, exprType);
		errors = new ArrayList<String>();
		line = nameTree.getLine();
		name = nameTree.getText();
		memberaccess = new ArrayList<String>();
		arguments = null;
		arrayAccess = null;
		type = null;
		leftEmit = "";
				
		if(nameTree.getChildCount() > 0)
		{
			for (int i = 0; i < nameTree.getChildCount(); i++)
			{
				Tree child = nameTree.getChild(i);
				
				if (child.getType() == MintLexer.DOT)
				{
					memberaccess.add(child.getChild(0).getText());
				} else if (child.getType() == MintLexer.ARRAYACCESS)
				{
					arrayAccess = new ExprNode(theParent, ExpressionType.Right, child.getChild(0));
				} else if (child.getType() == MintLexer.CALLMETHOD)
				{
					arguments = new ArrayList<Expression>();
					Tree argumentTree = child.getChild(0);
					CounterManager.getInstance().incrementParameter();
					
					if (argumentTree != null)
						for (int j = 0; j < argumentTree.getChildCount(); j++)
						{
							arguments.add(new ExprNode(theParent, ExpressionType.Right, argumentTree.getChild(j)));
							CounterManager.getInstance().incrementParameter();
						}
				}
			}
		}
	}

	@Override
	public void testSemantic() {
		Symbol symbol = searchSymbol(name);
		
		if (name.equals("shout"))
		{
			type = Type.voidType;
			if (arguments == null)
				errors.add(line + ": expected method parameters");
			else
				for(Expression expr : arguments)
				{
					expr.testSemantic();
				}
			
		} else
			processSymbol(symbol, 0);
	}

	private void processSymbol(Symbol symbol, int firstAccess) {
		if (symbol == null)
		{
			errors.add(line + ": " + name + " isn't defined in this scope");			
			type = Type.intType;
		}
		else
		{
			if (symbol.getSymbolType() == SymbolType.Class)
			{
				errors.add(line + ": " + name + " is a Type");
				type = Type.intType;
			}
			else
			{
				if (symbol.getSymbolType() == SymbolType.Method)
				{
					if (getExpressionType() == ExpressionType.Left || getExpressionType() == ExpressionType.RightLeft)
						errors.add(line + ": method invocation must be on the right side of a assignation");
					else if (arrayAccess != null || firstAccess < memberaccess.size() || arguments == null)
						errors.add(line + ": expected method parameters");					
					else
					{
						MethodSymbol methodSymbol = (MethodSymbol)symbol;
						for(Expression expr : arguments)
							expr.testSemantic();
						
						if (methodSymbol.getParameterListSize() != arguments.size())
							errors.add(line + ": " + methodSymbol.getName() + " constructor take " + 
										methodSymbol.getParameterListSize() + " but received " +
										arguments.size() + " arguments");
						else
						{
							for (int i = 0; i < methodSymbol.getParameterListSize(); i++)
							{
								if (methodSymbol.getParameter(i).getType().equals(Type.floatType))
								{
									if(!(arguments.get(i).getType().equals(Type.intType) || arguments.get(i).getType().equals(Type.floatType)))
										errors.add(line + ": " + methodSymbol.getName() + " " + i + 
												"th constructor argument expected type: " + methodSymbol.getParameter(i).getType().getCompleteName()
												+ " received type: " + arguments.get(i).getType().getCompleteName());
								} else	if (!methodSymbol.getParameter(i).getType().equals(arguments.get(i).getType()))
									if(!arguments.get(i).getType().equals(Type.objectNullType))
										errors.add(line + ": " + methodSymbol.getName() + " " + i + 
											"th constructor argument expected type: " + methodSymbol.getParameter(i).getType().getCompleteName()
											+ " received type: " + arguments.get(i).getType().getCompleteName());
							}
						}
						type = methodSymbol.getType();
					}
						
				} else if (symbol.getSymbolType() == SymbolType.Field)
				{
					processSymbolName(symbol.getName(), ((FieldSymbol)symbol).getType(), firstAccess);
				} else if (symbol.getSymbolType() == SymbolType.Parameter)
				{
					processSymbolName(symbol.getName(), ((ParameterSymbol)symbol).getType(), firstAccess);
				} else if (symbol.getSymbolType() == SymbolType.Variable)
				{
					processSymbolName(symbol.getName(), ((VariableSymbol)symbol).getType(), firstAccess);
				}
			}
		}
	}

	private void processSymbolName(String symbolName, Type symbolType, int firstAccess) {
		type = symbolType;
		if (symbolType.getTypeId() == TypeId.Primitive)
		{
			if (arrayAccess != null || firstAccess < memberaccess.size() || arguments != null)
				errors.add(line + ": invalid operators on primitive " + symbolName);							
		}
		else if (symbolType.getTypeId() == TypeId.PrimitiveArray)
		{
			
			if (firstAccess < memberaccess.size() || arguments != null)
				errors.add(line + ": " + symbolName + " expected array accessor");
			else
			{
				if (arrayAccess != null)
				{
					arrayAccess.testSemantic();
					if (!arrayAccess.getType().equals(Type.intType))
						errors.add(line + ": array accessor mus't be int type");
					else
					{
						type = symbolType.reduceIndex();
						/*if (symbolType.equals(Type.booleanArrayType))
							type = Type.booleanType;
						else if (symbolType.equals(Type.floatArrayType))
							type = Type.floatType;
						else
							type = Type.intType;*/
					}
				}
			}
		}
		else if (symbolType.getTypeId() == TypeId.ObjectArray)
		{
			if (firstAccess < memberaccess.size() || arguments != null)
				errors.add(line + ": " + symbolName + " expected array accessor");
			else
			{
				if (arrayAccess != null)
				{
					arrayAccess.testSemantic();
					if (!arrayAccess.getType().equals(Type.intType))
						errors.add(line + ": array accessor mus't be int type");
					else
					{
						type = Type.createObjectType(symbolType.getName());
					}
				}
			}
		}
		else if (symbolType.getTypeId() == TypeId.Object)
		{
			if (firstAccess < memberaccess.size())
			{
				String nextField = memberaccess.get(firstAccess);
				Symbol classSym = searchSymbol(symbolType.getName());				
				if (classSym == null)
					errors.add(line + ": Error raro al buscar la clase");
				else
					if (classSym.getSymbolType() != SymbolType.Class)
						errors.add(line + ": Error raro al buscar la clase");
					else
					{
						ClassSymbol theClass = (ClassSymbol)classSym;
						Symbol field = theClass.getClassNode().searchSymbol(nextField, getInnerClass(), false);
						
						processSymbol(field, firstAccess + 1);
					}
			}
			else
			{
				type = symbolType;
			}
		}
		
	}

	@Override
	public List<String> getSemanticErrors() {	
		if (arrayAccess != null)
			errors.addAll(arrayAccess.getSemanticErrors());
		if (arguments != null)
			for(Expression expr : arguments)
				errors.addAll(expr.getSemanticErrors());
		return errors;
	}

	@Override
	public void emit(Formatter output) {
		if(name.equals("shout"))
		{
			output.format(";Process shout\n");
			output.format("getstatic java/lang/System/out Ljava/io/PrintStream;\n");
			output.format("new java/lang/StringBuffer\n");
			output.format("dup\n");
			output.format("invokespecial java/lang/StringBuffer/<init>()V\n");			
			for (Expression expr : arguments)
			{
				expr.emit(output);
				output.format("invokevirtual java/lang/StringBuffer/append(%s)Ljava/lang/StringBuffer;\n", 
						expr.getType().emit());				
			}
			output.format("invokevirtual java/lang/StringBuffer/toString()Ljava/lang/String;\n");
			output.format("invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V\n");
		}
		else
		{
			Symbol symbol = searchSymbol(name);			
			
			if (symbol.getSymbolType() == SymbolType.Method)
			{
				MethodSymbol method = (MethodSymbol)symbol;
				
				if (arguments != null)
				{
					output.format("aload 0\n");
					FieldSymbol thisSymbol = (FieldSymbol)searchSymbol("this");
					String actualClass = thisSymbol.getType().emitTypeName();
					
					int i = 0;
					for (Expression expr : arguments)
					{
						expr.emit(output);
						if (method.getParameter(i).getType().equals(Type.floatType))
							if(expr.getType().equals(Type.intType))
								output.format("i2f\n");
					}
					
					output.format("invokevirtual %s/%s(", actualClass, method.getName());
					
					for (i = 0; i < method.getParameterListSize(); i++)
						output.format("%s", method.getParameter(i).getType().emit());
					output.format(")%s\n", method.getType().emit());
				}
			}
			else if (symbol.getSymbolType() == SymbolType.Field)
			{
				emitField(output, symbol);
			}
			else if (symbol.getSymbolType() == SymbolType.Parameter)
			{
				emitParameter(output, symbol);
			}
			else if (symbol.getSymbolType() == SymbolType.Variable)
			{
				emitVariable(output, symbol);
			}
		}
	}

	private void emitVariable(Formatter output, Symbol symbol) {
		VariableSymbol variable = (VariableSymbol)symbol;
		
		if (memberaccess.size() == 0)
		{
			if (getExpressionType() == ExpressionType.Right)
			{
				if (variable.getType().equals(Type.intType) || variable.getType().equals(Type.booleanType))
				{
					output.format("iload %s\n", variable.getNumber());					
				} else if (variable.getType().equals(Type.floatType))
				{
					output.format("fload %s\n", variable.getNumber());
				}
				else
				{
					output.format("aload %s\n", variable.getNumber());
					if (arrayAccess != null)
					{
						arrayAccess.emit(output);
						
						if ((variable.getType().equals(Type.intArrayType) || variable.getType().equals(Type.booleanArrayType)) && variable.getType().getDimension() <= 1)
							output.format("iaload\n");
						else if (variable.getType().equals(Type.floatArrayType) && variable.getType().getDimension() <= 1)
							output.format("faload\n");
						else
							output.format("aaload\n");
					}
				}
			}
			else if (getExpressionType() == ExpressionType.Left)
			{
				if(arrayAccess == null)
				{
					if (variable.getType().equals(Type.intType) || variable.getType().equals(Type.booleanType))
					{
						leftEmit = String.format("istore %s\n", variable.getNumber());					
					} else if (variable.getType().equals(Type.floatType))
					{
						leftEmit = String.format("fstore %s\n", variable.getNumber());
					}
					else
						leftEmit = String.format("astore %s\n", variable.getNumber());
				}
				else
				{
					output.format("aload %s\n", variable.getNumber());
					arrayAccess.emit(output);					
						
					if ((variable.getType().equals(Type.intArrayType) || variable.getType().equals(Type.booleanArrayType)) && variable.getType().getDimension() <= 1)
						leftEmit = String.format("iastore\n");
					else if (variable.getType().equals(Type.floatArrayType) && variable.getType().getDimension() <= 1)
						leftEmit = String.format("fastore\n");
					else
						leftEmit = String.format("aastore\n");				
					
				}
			}
			else
			{
				if(arrayAccess == null)
				{
					if (variable.getType().equals(Type.intType) || variable.getType().equals(Type.booleanType))
					{
						leftEmit = String.format("istore %s\n", variable.getNumber());
						leftEmit += String.format("iload %s\n", variable.getNumber());
					} else if (variable.getType().equals(Type.floatType))
					{
						leftEmit = String.format("fstore %s\n", variable.getNumber());
						leftEmit += String.format("fload %s\n", variable.getNumber());
					}
					else
					{
						leftEmit = String.format("astore %s\n", variable.getNumber());
						leftEmit += String.format("aload %s\n", variable.getNumber());
					}
				}
				else
				{
					output.format("aload %s\n", variable.getNumber());
					arrayAccess.emit(output);
						
					if ((variable.getType().equals(Type.intArrayType) || variable.getType().equals(Type.booleanArrayType)) && variable.getType().getDimension() <= 1)
						leftEmit = String.format("iastore\n");
					else if (variable.getType().equals(Type.floatArrayType) && variable.getType().getDimension() <= 1)
						leftEmit = String.format("fastore\n");
					else
						leftEmit = String.format("aastore\n");
					
					leftEmit += String.format("aload %s\n", variable.getNumber());
					
					OutputStream auxOutput = new OutputStream() {
						private String cad = "";
						
						@Override
						public void write(int b) throws IOException {
							cad += (char)b;
						}
						
						@Override
						public String toString() {
							return cad;
						}
					};
					
					Formatter auxFormatter = new Formatter(auxOutput);
					
					arrayAccess.emit(auxFormatter);
					leftEmit += auxOutput.toString(); //dar una buena revisada a esto O.o
					
					if ((variable.getType().equals(Type.intArrayType) || variable.getType().equals(Type.booleanArrayType)) && variable.getType().getDimension() <= 1)
						leftEmit += String.format("iastore\n");
					else if (variable.getType().equals(Type.floatArrayType) && variable.getType().getDimension() <= 1)
						leftEmit += String.format("fastore\n");
					else
						leftEmit += String.format("aastore\n");
				}
			}
		}
		else //Con acceso a miembros
		{
			String accessors = String.format("aload %s\n", variable.getNumber());
			String actualClass = variable.getType().emitTypeName();
			String actualClassName = variable.getType().getName();
			ClassSymbol classSymbol = (ClassSymbol)searchSymbol(actualClassName);
			Symbol actualSymbol = classSymbol.getClassNode().searchSymbol(memberaccess.get(0), getInnerClass(), false); 
			
			for(int i = 0; i < memberaccess.size() - 1; i++)
			{				
				accessors += String.format("getfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
				
				
				classSymbol = (ClassSymbol)searchSymbol(actualClassName);				
				if (actualSymbol.getSymbolType() == SymbolType.Method)
				{
					actualClass = ((MethodSymbol)actualSymbol).getType().emitTypeName();
					actualClassName = ((MethodSymbol)actualSymbol).getType().getName();
				}
				else
				{
					actualClass = ((FieldSymbol)actualSymbol).getType().emitTypeName();
					actualClassName = ((FieldSymbol)actualSymbol).getType().getName();
				}
				actualSymbol = classSymbol.getClassNode().searchSymbol(memberaccess.get(i + 1), getInnerClass(), false);
			}
			
			output.format(accessors);
			
			if (actualSymbol.getSymbolType() == SymbolType.Method)
			{
				MethodSymbol method = (MethodSymbol)actualSymbol;
				
				if (arguments != null)
				{
					int i = 0;
					for (Expression expr : arguments)
					{
						expr.emit(output);
						if (method.getParameter(i).getType().equals(Type.floatType))
							if(expr.getType().equals(Type.intType))
								output.format("i2f\n");
					}
					
					output.format("invokevirtual %s/%s(", actualClass, method.getName());
					
					for (i = 0; i < method.getParameterListSize(); i++)
						output.format("%s", method.getParameter(i).getType().emit());
					output.format(")%s\n", method.getType().emit());
				}
			}
			else //Ultimo objeto es un campo
			{
				FieldSymbol field = (FieldSymbol)actualSymbol;
				
				if (getExpressionType() == ExpressionType.Right)
				{
					output.format("getfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
					if (arrayAccess != null)
					{
						arrayAccess.emit(output);
						
						if ((field.getType().equals(Type.intArrayType) || field.getType().equals(Type.booleanArrayType)) && field.getType().getDimension() <= 1)
							output.format("iaload\n");
						else if (field.getType().equals(Type.floatArrayType) && field.getType().getDimension() <= 1)
							output.format("faload\n");
						else
							output.format("aaload\n");
					}
				}
				else if (getExpressionType() == ExpressionType.Left)
				{
					if(arrayAccess == null)
						leftEmit = String.format("putfield %s/%s %s", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
					else
					{
						output.format("getfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
						arrayAccess.emit(output);
						
						if ((field.getType().equals(Type.intArrayType) || field.getType().equals(Type.booleanArrayType)) && field.getType().getDimension() <= 1)
							leftEmit = String.format("iastore");
						else if (field.getType().equals(Type.floatArrayType)&& field.getType().getDimension() <= 1)
							leftEmit = String.format("fastore");
						else
							leftEmit = String.format("aastore");
					}
				}
				else
				{
					if(arrayAccess == null)
					{
						leftEmit = String.format("putfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
						leftEmit += String.format("getfield %s/%s %s", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
					}
					else
					{
						output.format("getfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
						arrayAccess.emit(output);
						
						if ((field.getType().equals(Type.intArrayType) || field.getType().equals(Type.booleanArrayType)) && field.getType().getDimension() <= 1)
							leftEmit = String.format("iastore\n");
						else if (field.getType().equals(Type.floatArrayType)&& field.getType().getDimension() <= 1)
							leftEmit = String.format("fastore\n");
						else
							leftEmit = String.format("aastore\n");
						
						leftEmit += String.format("getfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
						
						OutputStream auxOutput = new OutputStream() {
							private String cad = "";
							
							@Override
							public void write(int b) throws IOException {
								cad += (char)b;
							}
							
							@Override
							public String toString() {
								return cad;
							}
						};
						
						Formatter auxFormatter = new Formatter(auxOutput);
						
						arrayAccess.emit(auxFormatter);
						leftEmit += auxOutput.toString(); //dar una buena revisada a esto O.o
						if ((field.getType().equals(Type.intArrayType) || field.getType().equals(Type.booleanArrayType)) && field.getType().getDimension() <= 1)
							leftEmit += String.format("iaload");
						else if (field.getType().equals(Type.floatArrayType) && field.getType().getDimension() <= 1)
							leftEmit += String.format("faload");
						else
							leftEmit += String.format("aaload");
					}
				}
			}
		}
		
	}

	private void emitParameter(Formatter output, Symbol symbol) {
		ParameterSymbol parameter = (ParameterSymbol)symbol;
		
		if (memberaccess.size() == 0)
		{
			if (getExpressionType() == ExpressionType.Right)
			{
				if (parameter.getType().equals(Type.intType) || parameter.getType().equals(Type.booleanType))
				{
					output.format("iload %s\n", parameter.getNumber());					
				} else if (parameter.getType().equals(Type.floatType))
				{
					output.format("fload %s\n", parameter.getNumber());
				}
				else
				{
					output.format("aload %s\n", parameter.getNumber());
					if (arrayAccess != null)
					{
						arrayAccess.emit(output);
						
						if ((parameter.getType().equals(Type.intArrayType) || parameter.getType().equals(Type.booleanArrayType)) && parameter.getType().getDimension() <= 1)
							output.format("iaload\n");
						else if (parameter.getType().equals(Type.floatArrayType)&& parameter.getType().getDimension() <= 1)
							output.format("faload\n");
						else
							output.format("aaload\n");
					}
				}
			}
			else if (getExpressionType() == ExpressionType.Left)
			{
				if(arrayAccess == null)
				{
					if (parameter.getType().equals(Type.intType) || parameter.getType().equals(Type.booleanType))
					{
						leftEmit = String.format("istore %s\n", parameter.getNumber());					
					} else if (parameter.getType().equals(Type.floatType))
					{
						leftEmit = String.format("fstore %s\n", parameter.getNumber());
					}
					else
						leftEmit = String.format("astore %s\n", parameter.getNumber());
				}
				else
				{
					output.format("aload %s\n", parameter.getNumber());
					arrayAccess.emit(output);
						
					if ((parameter.getType().equals(Type.intArrayType) || parameter.getType().equals(Type.booleanArrayType)) && parameter.getType().getDimension() <= 1)
						leftEmit = String.format("iastore\n");
					else if (parameter.getType().equals(Type.floatArrayType)&& parameter.getType().getDimension() <= 1)
						leftEmit = String.format("fastore\n");
					else
						leftEmit = String.format("aastore\n");				
					
				}
			}
			else
			{
				if(arrayAccess == null)
				{
					if (parameter.getType().equals(Type.intType) || parameter.getType().equals(Type.booleanType))
					{
						leftEmit = String.format("istore %s\n", parameter.getNumber());
						leftEmit += String.format("iload %s\n", parameter.getNumber());
					} else if (parameter.getType().equals(Type.floatType))
					{
						leftEmit = String.format("fstore %s\n", parameter.getNumber());
						leftEmit += String.format("fload %s\n", parameter.getNumber());
					}
					else
					{
						leftEmit = String.format("astore %s\n", parameter.getNumber());
						leftEmit += String.format("aload %s\n", parameter.getNumber());
					}
				}
				else
				{
					output.format("aload %s\n", parameter.getNumber());
					arrayAccess.emit(output);
						
					if ((parameter.getType().equals(Type.intArrayType) || parameter.getType().equals(Type.booleanArrayType)) && parameter.getType().getDimension() <= 1)
						leftEmit = String.format("iastore\n");
					else if (parameter.getType().equals(Type.floatArrayType)&& parameter.getType().getDimension() <= 1)
						leftEmit = String.format("fastore\n");
					else
						leftEmit = String.format("aastore\n");
					
					leftEmit += String.format("aload %s\n", parameter.getNumber());
					
					OutputStream auxOutput = new OutputStream() {
						private String cad = "";
						
						@Override
						public void write(int b) throws IOException {
							cad += (char)b;
						}
						
						@Override
						public String toString() {
							return cad;
						}
					};
					
					Formatter auxFormatter = new Formatter(auxOutput);
					
					arrayAccess.emit(auxFormatter);
					leftEmit += auxOutput.toString(); //dar una buena revisada a esto O.o
					
					if ((parameter.getType().equals(Type.intArrayType) || parameter.getType().equals(Type.booleanArrayType)) && parameter.getType().getDimension() <= 1)
						leftEmit += String.format("iastore\n");
					else if (parameter.getType().equals(Type.floatArrayType)&& parameter.getType().getDimension() <= 1)
						leftEmit += String.format("fastore\n");
					else
						leftEmit += String.format("aastore\n");
				}
			}
		}
		else //Con acceso a miembros
		{
			String accessors = String.format("aload %s\n", parameter.getNumber());
			String actualClass = parameter.getType().emitTypeName();
			String actualClassName = parameter.getType().getName();
			ClassSymbol classSymbol = (ClassSymbol)searchSymbol(actualClassName);
			Symbol actualSymbol = classSymbol.getClassNode().searchSymbol(memberaccess.get(0), getInnerClass(), false); 
			
			for(int i = 0; i < memberaccess.size() - 1; i++)
			{
				accessors += String.format("getfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
				
				
				classSymbol = (ClassSymbol)searchSymbol(actualClassName);				
				if (actualSymbol.getSymbolType() == SymbolType.Method)
				{
					actualClass = ((MethodSymbol)actualSymbol).getType().emitTypeName();
					actualClassName = ((MethodSymbol)actualSymbol).getType().getName();
				}
				else
				{
					actualClass = ((FieldSymbol)actualSymbol).getType().emitTypeName();
					actualClassName = ((FieldSymbol)actualSymbol).getType().getName();
				}
				actualSymbol = classSymbol.getClassNode().searchSymbol(memberaccess.get(i + 1), getInnerClass(), false);
			}
			
			output.format(accessors);
			
			if (actualSymbol.getSymbolType() == SymbolType.Method)
			{
				MethodSymbol method = (MethodSymbol)actualSymbol;
				
				if (arguments != null)
				{
					int i = 0;
					for (Expression expr : arguments)
					{
						expr.emit(output);
						if (method.getParameter(i).getType().equals(Type.floatType))
							if(expr.getType().equals(Type.intType))
								output.format("i2f\n");
					}
					
					output.format("invokevirtual %s/%s(", actualClass, method.getName());
					
					for (i = 0; i < method.getParameterListSize(); i++)
						output.format("%s", method.getParameter(i).getType().emit());
					output.format(")%s\n", method.getType().emit());
				}
			}
			else //Ultimo objeto es un campo
			{
				FieldSymbol field = (FieldSymbol)actualSymbol;
				
				if (getExpressionType() == ExpressionType.Right)
				{
					output.format("getfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
					if (arrayAccess != null)
					{
						arrayAccess.emit(output);
						
						if ((parameter.getType().equals(Type.intArrayType) || parameter.getType().equals(Type.booleanArrayType)) && parameter.getType().getDimension() <= 1)
							output.format("iaload\n");
						else if (field.getType().equals(Type.floatArrayType)&& parameter.getType().getDimension() <= 1)
							output.format("faload\n");
						else
							output.format("aaload\n");
					}
				}
				else if (getExpressionType() == ExpressionType.Left)
				{
					if(arrayAccess == null)
						leftEmit = String.format("putfield %s/%s %s", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
					else
					{
						output.format("getfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
						arrayAccess.emit(output);
						
						if ((parameter.getType().equals(Type.intArrayType) || parameter.getType().equals(Type.booleanArrayType)) && parameter.getType().getDimension() <= 1)
							leftEmit = String.format("iastore");
						else if (field.getType().equals(Type.floatArrayType)&& parameter.getType().getDimension() <= 1)
							leftEmit = String.format("fastore");
						else
							leftEmit = String.format("aastore");
					}
				}
				else
				{
					if(arrayAccess == null)
					{
						leftEmit = String.format("putfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
						leftEmit += String.format("getfield %s/%s %s", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
					}
					else
					{
						output.format("getfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
						arrayAccess.emit(output);
						
						if ((parameter.getType().equals(Type.intArrayType) || parameter.getType().equals(Type.booleanArrayType)) && parameter.getType().getDimension() <= 1)
							leftEmit = String.format("iastore\n");
						else if (field.getType().equals(Type.floatArrayType)&& parameter.getType().getDimension() <= 1)
							leftEmit = String.format("fastore\n");
						else
							leftEmit = String.format("aastore\n");
						
						leftEmit += String.format("getfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
						
						OutputStream auxOutput = new OutputStream() {
							private String cad = "";
							
							@Override
							public void write(int b) throws IOException {
								cad += (char)b;
							}
							
							@Override
							public String toString() {
								return cad;
							}
						};
						
						Formatter auxFormatter = new Formatter(auxOutput);
						
						arrayAccess.emit(auxFormatter);
						leftEmit += auxOutput.toString(); //dar una buena revisada a esto O.o
						if ((parameter.getType().equals(Type.intArrayType) || parameter.getType().equals(Type.booleanArrayType)) && parameter.getType().getDimension() <= 1)
							leftEmit += String.format("iaload");
						else if (field.getType().equals(Type.floatArrayType) && parameter.getType().getDimension() <= 1)
							leftEmit += String.format("faload");
						else
							leftEmit += String.format("aaload");
					}
				}
			}
		}
	}

	private void emitField(Formatter output, Symbol symbol) {
		FieldSymbol field = (FieldSymbol)symbol;
		FieldSymbol thisSymbol = (FieldSymbol)searchSymbol("this");
				
		output.format("aload 0\n");			
		
		if (memberaccess.size() == 0)
		{
			if (getExpressionType() == ExpressionType.Right)
			{
				output.format("getfield %s/%s %s\n", thisSymbol.getType().emitTypeName(), field.getName(), field.getType().emit());
				if (arrayAccess != null)
				{
					arrayAccess.emit(output);
					
					if ((field.getType().equals(Type.intArrayType) || field.getType().equals(Type.booleanArrayType)) && field.getType().getDimension() <= 1)
						output.format("iaload\n");
					else if (field.getType().equals(Type.floatArrayType) && field.getType().getDimension() <= 1)
						output.format("faload\n");
					else
						output.format("aaload\n");
				}
			}
			else if (getExpressionType() == ExpressionType.Left)
			{
				if(arrayAccess == null)
					leftEmit = String.format("putfield %s/%s %s", thisSymbol.getType().emitTypeName(), field.getName(), field.getType().emit());
				else
				{
					output.format("getfield %s/%s %s\n", thisSymbol.getType().emitTypeName(), field.getName(), field.getType().emit());
					arrayAccess.emit(output);
					
					if ((field.getType().equals(Type.intArrayType) || field.getType().equals(Type.booleanArrayType)) && field.getType().getDimension() <= 1)
						leftEmit = String.format("iastore");
					else if (field.getType().equals(Type.floatArrayType) && field.getType().getDimension() <= 1)
						leftEmit = String.format("fastore");
					else
						leftEmit = String.format("aastore");
				}
			}
			else
			{
				if(arrayAccess == null)
				{
					leftEmit = String.format("putfield %s/%s %s\n", thisSymbol.getType().emitTypeName(), field.getName(), field.getType().emit());
					leftEmit += String.format("getfield %s/%s %s", thisSymbol.getType().emitTypeName(), field.getName(), field.getType().emit());
				}
				else
				{
					output.format("getfield %s/%s %s\n", thisSymbol.getType().emitTypeName(), field.getName(), field.getType().emit());
					arrayAccess.emit(output);
					
					if ((field.getType().equals(Type.intArrayType) || field.getType().equals(Type.booleanArrayType)) && field.getType().getDimension() <= 1)
						leftEmit = String.format("iastore\n");
					else if (field.getType().equals(Type.floatArrayType) && field.getType().getDimension() <= 1)
						leftEmit = String.format("fastore\n");
					else
						leftEmit = String.format("aastore\n");
					
					leftEmit += String.format("getfield %s/%s %s\n", thisSymbol.getType().emitTypeName(), field.getName(), field.getType().emit());
					
					OutputStream auxOutput = new OutputStream() {
						private String cad = "";
						
						@Override
						public void write(int b) throws IOException {
							cad += (char)b;
						}
						
						@Override
						public String toString() {
							return cad;
						}
					};
					
					Formatter auxFormatter = new Formatter(auxOutput);
					
					arrayAccess.emit(auxFormatter);
					leftEmit += auxOutput.toString(); //dar una buena revisada a esto O.o
					if ((field.getType().equals(Type.intArrayType) || field.getType().equals(Type.booleanArrayType)) && field.getType().getDimension() <= 1)
						leftEmit += String.format("iaload");
					else if (field.getType().equals(Type.floatArrayType) && field.getType().getDimension() <= 1)
						leftEmit += String.format("faload");
					else
						leftEmit += String.format("aaload");
				}
			}
		}
		else
		{
			String accessors = String.format("getfield %s/%s %s\n", thisSymbol.getType().emitTypeName(), field.getName(), field.getType().emit());
			String actualClass = thisSymbol.getType().emitTypeName();
			String actualClassName = thisSymbol.getType().getName();
			Symbol actualSymbol = field; 
			
			for(int i = 0; i < memberaccess.size(); i++)
			{
				if (i < memberaccess.size() - 1)
					accessors += String.format("getfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
				
				ClassSymbol classSymbol = (ClassSymbol)searchSymbol(actualClassName);
				
				if (actualSymbol.getSymbolType() == SymbolType.Method)
				{
					actualClass = ((MethodSymbol)actualSymbol).getType().emitTypeName();
					actualClassName = ((MethodSymbol)actualSymbol).getType().getName();
				}
				else
				{
					actualClass = ((FieldSymbol)actualSymbol).getType().emitTypeName();
					actualClassName = ((FieldSymbol)actualSymbol).getType().getName();
				}
				actualSymbol = classSymbol.getClassNode().searchSymbol(memberaccess.get(i), getInnerClass(), false);
			}
			
			output.format(accessors);
			
			if (actualSymbol.getSymbolType() == SymbolType.Method)
			{
				MethodSymbol method = (MethodSymbol)actualSymbol;
				
				if (arguments != null)
				{
					int i = 0;
					for (Expression expr : arguments)
					{
						expr.emit(output);
						if (method.getParameter(i).getType().equals(Type.floatType))
							if(expr.getType().equals(Type.intType))
								output.format("i2f\n");
					}
					
					output.format("invokevirtual %s/%s(", actualClass, method.getName());
					
					for (i = 0; i < method.getParameterListSize(); i++)
						output.format("%s", method.getParameter(i).getType().emit());
					output.format(")%s\n", method.getType().emit());
				}
			}
			else //Ultimo objeto es un campo
			{
				field = (FieldSymbol)actualSymbol;
				if (getExpressionType() == ExpressionType.Right)
				{
					output.format("getfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
					if (arrayAccess != null)
					{
						arrayAccess.emit(output);
						
						if ((field.getType().equals(Type.intArrayType) || field.getType().equals(Type.booleanArrayType)) && field.getType().getDimension() <= 1)
							output.format("iaload\n");
						else if (field.getType().equals(Type.floatArrayType) && field.getType().getDimension() <= 1)
							output.format("faload\n");
						else
							output.format("aaload\n");
					}
				}
				else if (getExpressionType() == ExpressionType.Left)
				{
					if(arrayAccess == null)
						leftEmit = String.format("putfield %s/%s %s", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
					else
					{
						output.format("getfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
						arrayAccess.emit(output);
						
						if ((field.getType().equals(Type.intArrayType) || field.getType().equals(Type.booleanArrayType)) && field.getType().getDimension() <= 1)
							leftEmit = String.format("iastore");
						else if (field.getType().equals(Type.floatArrayType) && field.getType().getDimension() <= 1)
							leftEmit = String.format("fastore");
						else
							leftEmit = String.format("aastore");
					}
				}
				else
				{
					if(arrayAccess == null)
					{
						leftEmit = String.format("putfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
						leftEmit += String.format("getfield %s/%s %s", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
					}
					else
					{
						output.format("getfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
						arrayAccess.emit(output);
						
						if ((field.getType().equals(Type.intArrayType) || field.getType().equals(Type.booleanArrayType)) && field.getType().getDimension() <= 1)
							leftEmit = String.format("iastore\n");
						else if (field.getType().equals(Type.floatArrayType) && field.getType().getDimension() <= 1)
							leftEmit = String.format("fastore\n");
						else
							leftEmit = String.format("aastore\n");
						
						leftEmit += String.format("getfield %s/%s %s\n", actualClass, actualSymbol.getName(), ((FieldSymbol)actualSymbol).getType().emit());
						
						OutputStream auxOutput = new OutputStream() {
							private String cad = "";
							
							@Override
							public void write(int b) throws IOException {
								cad += (char)b;
							}
							
							@Override
							public String toString() {
								return cad;
							}
						};
						
						Formatter auxFormatter = new Formatter(auxOutput);
						
						arrayAccess.emit(auxFormatter);
						leftEmit += auxOutput.toString(); //dar una buena revisada a esto O.o
						if ((field.getType().equals(Type.intArrayType) || field.getType().equals(Type.booleanArrayType)) && field.getType().getDimension() <= 1)
							leftEmit += String.format("iaload");
						else if (field.getType().equals(Type.floatArrayType) && field.getType().getDimension() <= 1)
							leftEmit += String.format("faload");
						else
							leftEmit += String.format("aaload");
					}
				}
			}
		}
	}
	
	@Override
	public void leftEmit(Formatter output) {
		output.format("%s\n", leftEmit);
	}

	@Override
	public int getLine() {
		return line;
	}

	@Override
	public Type getType() {
		return type;
	}

}
